package edu.kit.csl.cratyli.control;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Map.Entry;

import edu.kit.csl.cratyli.datatypes.BadParameterValueException;
import edu.kit.csl.cratyli.datatypes.Word;
import edu.kit.csl.cratyli.ui.Configuration;
import edu.kit.csl.cratyli.ui.Logger;

/**
 * This merger runs the nbest-lattice tool to merge the pronunciations.
 */
public class SrilmMerger extends Merger {
	
	private Merger fallbackMerger;
	
	private final String TMP_FILE = "/tmp/cratyli.tmp.srilm."
			+ System.currentTimeMillis();
	
	private String nbestLatticeCmd;
	
	private WritingSystem ws;

	public SrilmMerger() throws BadParameterValueException {
		super();
		fallbackMerger = new MaximumMerger();
		Configuration conf = Configuration.getSingleton();
		nbestLatticeCmd = conf.getString("mergerToolPath").trim();
		File file = new File(nbestLatticeCmd);
		if (!file.canExecute()) {
			throw new BadParameterValueException("The mergerToolPath parameter"
				+ " does not point to an executable file: " + nbestLatticeCmd);
		}
		nbestLatticeCmd += " " + conf.getString("mergerToolArgs").trim()
				+ " -nbest " + TMP_FILE;
		String l = conf.getString("writingSystem");
		if (l.equals("p2g")) {
			ws = new MappingWritingSystem();
		} else if (l.equals("dropBlanks")) {
			ws = new DropBlankWritingSystem();
		} else {
			Logger.getSingleton().fatalError("Writing system " + l
				+ " is not implemented. Fall back to dropBlanks writing "
				+ " system.");
			ws = new DropBlankWritingSystem();
		}
	}

	/* (non-Javadoc)
	 * @see Merger#merge(Word, java.util.Map)
	 */
	@Override
	public void merge(Word pWord, Map<Word, Double> sWords) {
		if (sWords.size() < 2) {
			fallbackMerger.merge(pWord, sWords);
			return;
		}
		Logger log = Logger.getSingleton();
		try {
			FileWriter fstream = new FileWriter(TMP_FILE);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write("(" + convertToBytelogScale(annealing.getTemperature()
					* pWord.getWeight()) + ") " 
					+ pWord.getStringPronunciation() + "\n");
			double scoreSum = 0.0;
			for (Entry<Word, Double> entry : sWords.entrySet()) {
				scoreSum += entry.getValue();
				out.write("(" + convertToBytelogScale(entry.getValue())
					+ ") " + entry.getKey().getStringPronunciation() + "\n");
			}
			out.close();
			String result = runNbestLattice();
			if (result != null) {
				result = result.trim();
				if (!result.isEmpty()) {
					pWord.setPronunciation(result.split(" "));
					String ortho = ws.convert(pWord);
					if (log.getLevel() <= Logger.DEBUG) {
						StringBuilder b = new StringBuilder();
						b.append(pWord.getOrthographic() + " ("
							+ pWord.getWeight() + ")");
						for (Entry<Word, Double> entry : sWords.entrySet()) {
							b.append(", " + entry.getKey().getOrthographic()
								+ " (" + entry.getValue() + ")");
						}
						b.append("   --->   " + ortho + " (" 
								+ (pWord.getWeight() + scoreSum) + ")");
						log.debug(b.toString());
					}
					pWord.setOrthographic(ortho);
					pWord.increaseWeight(scoreSum);
				}
			}
		} catch (IOException e) {
			log.error("IOException while executing nbest-lattice: " 
					+ e.getMessage());
		}
		catch (InterruptedException e) {
			log.error("Program execution of nbest-lattice interrupted: " 
					+ e.getMessage());
		}
	}
	

	/*
	 * Runs nbest-lattice and returns the result
	 */
	private String runNbestLattice() throws IOException, InterruptedException {
	      String ret = null;
	      Process p = Runtime.getRuntime().exec(nbestLatticeCmd);
	      BufferedReader bri = new BufferedReader
	        (new InputStreamReader(p.getInputStream()));
	      BufferedReader bre = new BufferedReader
	        (new InputStreamReader(p.getErrorStream()));
	      if ((ret = bri.readLine()) == null) {
	    	  Logger.getSingleton().error("nbest-lattice: no output");
	      } else if (bri.readLine() != null) {
	    	  Logger.getSingleton().error("nbest-lattice: multiple lines");
	    	  ret = null;
	      }
	      bri.close();
	      String line;
	      while ((line = bre.readLine()) != null) {
	    	  Logger.getSingleton().error("nbest-lattice stderr: " + line);
	      }
	      bre.close();
	      p.waitFor();
	      return ret;
	}
	
	/*
	 * nbest-lattice requires the scores in bytelog scale
	 */
	private double convertToBytelogScale(double x) {
		double logscale = 2.30258509299404568402 * 10000.5 / 1024.0;
		return Math.log10(x) * logscale;
	}
}
